package jtacck.labmedicine;

import java.util.Calendar;
import java.util.Iterator;

import jvm.jtacck.db.records.CheckListPair;
import jvm.jtacck.db.records.ClientSite;
import jvm.jtacck.db.records.ShippingInformation;
import jvm.jtacck.serials.Route;
import jvm.jtacck.serials.ShippingList;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;

/**
 * @author Kevin Anderson + Jesse Jeng + Andrew Lau
 * 
 *         Main Activity for the Android application, starts with the login
 *         screen where the user can login with correct username and password.
 *         The user can also access the preference screen through this activity
 */
public class MainActivity extends Activity {

    /**
     * Reference to the ServerLinker object, used for all necessary connections
     * to the Android server
     */
    protected static ServerLinker linker = null;

    /**
     * Username and password preferences
     */
    private static SharedPreferences pref;
    private static String PREF_USERNAME;
    private static String PREF_PASSWORD;
    private static String REMEMBER = "REMEMBER_INFO";

    /**
     * Checkbox for the "Remember me" functionality
     */
    private static CheckBox remember;

    /**
     * Text input box for username
     */
    private static EditText usernameField;

    /**
     * Text input box for password
     */
    private static EditText passwordField;

    /**
     * Route object that contains all the Hospital objects and ClientSite
     * objects, used for displaying the route screen
     */
    protected static Route route = null;

    /**
     * Reference to the currently selected ClientSite
     */
    protected static ClientSite currentSite = null;

    /**
     * Reference to the currently selected ShippingList
     */
    protected static ShippingList sList = null;
    protected static ShippingInformation sInfo = null;
    protected static Iterator<ClientSite> currentHospList;
    protected static CheckListPair routeCheckList;
    protected static final Cache cache = new Cache();

    protected static Thread t;

    /**
     * Location Manager that gives us access to features to retrieve locations
     */
    protected static LocationManager mLocManager;

    /**
     * Location listener used to listen for updates of location
     */
    protected LocationListener listener;

    /**
     * Whether GPS has been updated at least once upon login
     */
    protected static boolean hasUpdated;

    /** Called when the activity is first created. */

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onCreate(android.os.Bundle)
     * 
     * Handler for when the activity is first created. Creates all the input
     * boxes for the login screen and a checkbox that enables/diables the
     * 'Remember me' function
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Get the preferences used by the application and load defaults
        pref = PreferenceManager.getDefaultSharedPreferences(this);
        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);

        usernameField = (EditText) findViewById(R.id.username_input);
        passwordField = (EditText) findViewById(R.id.password_in);
        remember = (CheckBox) findViewById(R.id.checkBox1);

        hasUpdated = false;

        final LoginButton loginButton = new LoginButton(this, usernameField,
                passwordField);

        // See if check box was checked at any time
        if (pref.getBoolean(REMEMBER, false)) {
            remember.setChecked(true);
        }

        // Set the event handler to reset preferences if remember login is not
        // checked
        remember.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                if (!isChecked) {

                    PREF_USERNAME = "";
                    PREF_PASSWORD = "";
                    pref.edit().putString("PREF_USERNAME", PREF_USERNAME)
                            .putString("PREF_PASSWORD", PREF_PASSWORD).commit();
                }
            }
        });

        // Sets the event handler for when the user presses enter key in the
        // password input box
        passwordField.setOnKeyListener(new OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    findViewById(R.id.login_button).performClick();
                    return true;
                }
                return false;
            }
        });

        // Sets the event handler for when the user hits the 'submit' button
        findViewById(R.id.login_button).setOnClickListener(loginButton);

        mLocManager = (LocationManager) this
                .getSystemService(Context.LOCATION_SERVICE);

        listener = new LocationListener() {

            private static final int TWO_MINUTES = 1000 * 60 * 2;

            /**
             * Best GPS location we encounter so far
             */
            private Location bestSoFar;

            /**
             * Calendar object used to retrieve time
             */
            private Calendar c = Calendar.getInstance();

            /**
             * Time associated to the calendar object
             */
            private long time = c.getTimeInMillis();

            /**
             * Determines whether one Location reading is better than the
             * current Location fix
             * 
             * @param location
             *            The new Location that you want to evaluate
             * @param currentBestLocation
             *            The current Location fix, to which you want to compare
             *            the new one
             */
            protected boolean isBetterLocation(Location location,
                    Location currentBestLocation) {
                if (currentBestLocation == null) {
                    // A new location is always better than no location
                    return true;
                }

                // Check whether the new location fix is newer or older
                long timeDelta = location.getTime()
                        - currentBestLocation.getTime();
                boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
                boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
                boolean isBetweenNewerAndOlder = timeDelta < TWO_MINUTES / 2;
                boolean isNewer = timeDelta > 0;

                // If it's been more than two minutes since the current
                // location, use the new location
                // because the user has likely moved
                if (isSignificantlyNewer) {
                    return true;
                    // If the new location is more than two minutes older, it
                    // must be worse
                } else if (isSignificantlyOlder || isBetweenNewerAndOlder) {
                    return false;
                }

                // Check whether the new location fix is more or less accurate
                int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
                        .getAccuracy());
                boolean isLessAccurate = accuracyDelta > 0;
                boolean isMoreAccurate = accuracyDelta < 0;
                boolean isSignificantlyLessAccurate = accuracyDelta > 200;

                // Check if the old and new location are from the same provider
                boolean isFromSameProvider = isSameProvider(
                        location.getProvider(),
                        currentBestLocation.getProvider());

                // Determine location quality using a combination of timeliness
                // and accuracy
                if (isMoreAccurate) {
                    return true;
                } else if (isNewer && !isLessAccurate) {
                    return true;
                } else if (isNewer && !isSignificantlyLessAccurate
                        && isFromSameProvider) {
                    return true;
                }
                return false;
            }

            /** Checks whether two providers are the same */
            private boolean isSameProvider(String provider1, String provider2) {
                if (provider1 == null) {
                    return provider2 == null;
                }
                return provider1.equals(provider2);
            }

            /*
             * (non-Javadoc)
             * 
             * @see
             * android.location.LocationListener#onLocationChanged(android.location
             * .Location)
             * 
             * When we get a new location we want to update GPS if it is a
             * better location than our last
             */
            @Override
            public void onLocationChanged(Location location) {
                if (linker != null) {
                    if (bestSoFar != null) {
                        if (isBetterLocation(location, bestSoFar)) {
                            long time_now = c.getTimeInMillis();
                            Log.w("GPS", "SENDING TOO FAST");
                            Log.w("time", "Time is " + time_now);
                            Log.w("time", "Time was " + time);
                            linker.updateGPS(location.getLatitude(),
                                    location.getLongitude());
                            bestSoFar = location;
                            time = time_now;
                        }
                    } else {
                        bestSoFar = location;
                    }
                }

            }

            /*
             * (non-Javadoc)
             * 
             * @see
             * android.location.LocationListener#onProviderDisabled(java.lang
             * .String)
             * 
             * This method must be implemented as it as abstract. Currently we
             * have no use for it, so it is blank, but it must be implemented.
             */
            @Override
            public void onProviderDisabled(String provider) {

            }

            /*
             * (non-Javadoc)
             * 
             * @see
             * android.location.LocationListener#onProviderEnabled(java.lang
             * .String)
             * 
             * This method must be implemented as it as abstract. Currently we
             * have no use for it, so it is blank, but it must be implemented.
             */
            @Override
            public void onProviderEnabled(String provider) {

            }

            /*
             * (non-Javadoc)
             * 
             * @see android.location.LocationListener#onStatusChanged(java.lang.
             * String, int, android.os.Bundle)
             * 
             * This method must be implemented as it as abstract. Currently we
             * have no use for it, so it is blank, but it must be implemented.
             */
            @Override
            public void onStatusChanged(String provider, int status,
                    Bundle extras) {

            }

        };

        mLocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                60000, 0, listener);
        mLocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000,
                300, listener);

    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onPause()
     */
    @Override
    protected void onPause() {
        MainActivity.super.onPause();
        boolean isChecked = remember.isChecked();

        // Save values to preferences if user wants to remember their login,
        // else we just store the state of our check box
        if (isChecked) {
            PREF_USERNAME = usernameField.getText().toString();
            PREF_PASSWORD = passwordField.getText().toString();
            pref.edit().putString("PREF_USERNAME", PREF_USERNAME)
                    .putString("PREF_PASSWORD", PREF_PASSWORD)
                    .putBoolean(REMEMBER, true).commit();
        } else {
            pref.edit().putBoolean(REMEMBER, false).commit();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onResume()
     */
    @Override
    protected void onResume() {
        MainActivity.super.onResume();

        // Load values saved in preferences
        PREF_USERNAME = pref.getString("PREF_USERNAME", "");
        PREF_PASSWORD = pref.getString("PREF_PASSWORD", "");
        usernameField.setText(PREF_USERNAME);
        passwordField.setText(PREF_PASSWORD);
        if (pref.getBoolean(REMEMBER, false)) {
            remember.setChecked(true);
        } else {
            remember.setChecked(false);
        }

        mLocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                60000, 0, listener);
        mLocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000,
                0, listener);
        
        if (linker != null) {
            linker.disconnect();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate or create the menu
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
     * 
     * Event handler for the various actions user can perform after hitting the
     * menu button.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.main_text:

            // Load the preferences page
            startActivity(new Intent(this, PreferencesFromXml.class));
            break;
        case R.id.main_icontext:
            break;
        }
        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocManager.removeUpdates(listener);
        if (linker != null) {
            linker.disconnect();
        }
    }

}
